﻿#define TEST
//#define RENDER_TEST 
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Timers;
using FloodFill;

namespace LedCenter.GUI
{
    public partial class LETILed : UserControl
    {
        FloodFiller floodFilter = new FloodFiller();
        // LED pixel 1 color
        public struct LED_PIXEL_1COLOR
        {
            bool red; // on/off value
        }

        // LED pixel status
        public struct LED_PIXEL_STATUS
        {
            bool status; // on/off value
        }

        // LED pixel 3 color:
        // red = 1, green = 0 -> red
        // red = 0, green = 1 -> green
        // red = 1, green = 1 -> yellow
        public struct LED_PIXEL_3COLOR
        {
            public bool Red; // on/off value
            public bool Green;// on/off value

            public LED_PIXEL_3COLOR(bool red, bool green)
            {
                Red = red;
                Green = green;
            }
            public static bool operator ==(LED_PIXEL_3COLOR left, LED_PIXEL_3COLOR right)
            {
                return left.Red == right.Red && left.Green == right.Green;
            }
            public static bool operator !=(LED_PIXEL_3COLOR left, LED_PIXEL_3COLOR right)
            {
                return left.Red != right.Red && left.Green != right.Green;
            }
        }
        public static LED_PIXEL_3COLOR LED_PIXEL_3COLORTABLE_RED = new LED_PIXEL_3COLOR(true, false);
        public static LED_PIXEL_3COLOR LED_PIXEL_3COLORTABLE_GREEN = new LED_PIXEL_3COLOR(false, true);
        public static LED_PIXEL_3COLOR LED_PIXEL_3COLORTABLE_YELLOW = new LED_PIXEL_3COLOR(true, true);
        public static LED_PIXEL_3COLOR LED_PIXEL_3COLORTABLE_NONE = new LED_PIXEL_3COLOR(false, false);

        public struct LED_PIXEL_FULLCOLOR
        {
            // TODO: TBD   
        }

        LED_PIXEL_3COLOR[,] mLedMatrix;
        LED_PIXEL_3COLOR[,] mLedItemMatrix;
#if !TOANNV
        bool[,] mLedStatusMatrix;
#else
        LED_PIXEL_3COLOR[,] mLedStatusMatrix;
#endif

        int mPixelGapX = 0;// gap between pixels in vertical
        int mPixelGapY = 0;// gap between pixels in horizontal

        int mWidth = 160;// control width
        int mHeight = 160;// control height

        public static ushort BPP
        {
            get
            {
                byte bpp;
                unsafe
                {
                    bpp = (byte)sizeof(LETILed.LED_PIXEL_3COLOR);
                }
                return bpp;
            }
        }
        [Browsable(true)]
        [Category("Led Config")]
        public Size Resolution
        {
            get { return new Size(mXPixels, mYPixels); }
            set { 
                mXPixels = (ushort)value.Width; 
                mYPixels = (ushort)value.Height;
                initData();// init data base on new width and height
                this.Invalidate(); }
        }

        int mXPixels = 128;// real led width's pixel
        int mYPixels = 64;// real led height's pixel

        [Browsable(true)]
        [Category("Led Config")]
        public bool Blinking
        {
            get { return blinking; }
            set
            {
                blinking = value;
                if (blinking == true)
                {
                    if (tmBlink == null)
                    {
                        tmBlink = new System.Timers.Timer(500);
                        tmBlink.Elapsed += new ElapsedEventHandler(tmBlink_Elapsed);
                    }
                    tmBlink.AutoReset = true;
                    tmBlink.Start();
                    blinkingFlag = true;
                }
                else
                {
                    tmBlink.AutoReset = false;
                    tmBlink.Stop();
                    blinkingFlag = true;
                }

            }
        }
        private bool blinking = true;

        private bool blinkingFlag = false;
        bool dataChanged = false;

        private System.Timers.Timer tmBlink;
        public LETILed()
        {
            InitializeComponent();
            tmBlink = new System.Timers.Timer(250);
            tmBlink.Elapsed += new ElapsedEventHandler(tmBlink_Elapsed);

            FrameRate = 30;// force draw 30 frame per second
            Blinking = false;
            Index = 1;
            /*
            for (int i = 0; i < mXPixels; i++)
            {
                for (int j = 0; j < mYPixels; j++)
                {
                    if (j == 0)
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_RED;
                    }
                    else if (i == 7)
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_GREEN;
                    }
                    else
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_YELLOW;
                    }
                }
            }
             * */
            dataChanged = true;
            // init data
            initData();

        }

        private void initData()
        {
            mLedMatrix = new LED_PIXEL_3COLOR[mXPixels, mYPixels];
#if !TOANNV
            mLedStatusMatrix = new bool[mXPixels, mYPixels];
#else
                mLedStatusMatrix = new LED_PIXEL_3COLOR[mXPixels, mYPixels];
#endif

            // create 8x8 test item
            mLedItemMatrix = new LED_PIXEL_3COLOR[8, 8];

            for (int i = 0; i < mXPixels; i++)
            {
                for (int j = 0; j < mYPixels; j++)
                {
                    /*
                    if ((j % 6 == 0 && i % 3 == 0) || (i % 7 == 0 && i % 5 == 0))
                        mLedStatusMatrix[i, j] = false;
                    else 
                     * */
#if !TOANNV
                    mLedStatusMatrix[i, j] = true;
#else
                mLedStatusMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;
#endif

                }
            }
            Random random = new Random();
            int length = mXPixels * mYPixels;
            int index = 0;
            for (int i = 0; i < 10; i++)
            {
                // get random index
                index = random.Next(length);
                // set led status at index if false
#if !TOANNV
                mLedStatusMatrix[(int)(index % mXPixels), (int)(index / mXPixels)] = true; ;
#else
                mLedStatusMatrix[(int)(index % mXPixels), (int)(index / mXPixels)] = LED_PIXEL_3COLORTABLE_RED; ;
#endif

            }

            isDrag = false;
            // init item data
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    mLedItemMatrix[i, j] = LED_PIXEL_3COLORTABLE_RED;
                }
            }
            dataChanged = true;
        }

        public void initWithLedStatusData(ushort width, ushort height, byte[] ledStatusData)
        {
            mXPixels = width;
            mYPixels = height;
            BitArray bits = new BitArray(ledStatusData);
            mLedMatrix = new LED_PIXEL_3COLOR[mXPixels, mYPixels];
#if !TOANNV
            mLedStatusMatrix = new bool[mXPixels, mYPixels];
#else
            mLedStatusMatrix = new LED_PIXEL_3COLOR[mXPixels, mYPixels];
#endif
            mLedItemMatrix = new LED_PIXEL_3COLOR[8, 8];
            for (int i = 0; i < mXPixels; i++)
            {
                for (int j = 0; j < mYPixels; j++)
                {
#if !TOANNV
                    mLedStatusMatrix[i, j] = !bits[j * mXPixels + i];
#else
                    mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;
#endif
                }
            }
            dataChanged = true;
        }

        public void initWithLedData(ushort width, ushort height, byte[] ledStatusData)
        {
            mXPixels = width;
            mYPixels = height;
            int al;
            BitArray bits = new BitArray(ledStatusData);
            
            mLedMatrix = new LED_PIXEL_3COLOR[mXPixels, mYPixels];
#if !TOANNV
            mLedStatusMatrix = new bool[mXPixels, mYPixels];
#else
            mLedStatusMatrix = new LED_PIXEL_3COLOR[mXPixels, mYPixels];
#endif
            mLedItemMatrix = new LED_PIXEL_3COLOR[8, 8];
            StringBuilder content = new StringBuilder();
            int bitIndex = 0;
            for (int j = 0; j < mYPixels; j++)
            {
                for (int i = 0; i < mXPixels; i++, bitIndex+=2)
                {
#if !TOANNV
                    mLedStatusMatrix[i, j] = false;
#else
                        //content.Append(bits[bitIndex] + ", ");
                        //content.Append(bits[bitIndex + 1] + ", ");
                    mLedStatusMatrix[i, j].Red = bits[bitIndex];
                    mLedStatusMatrix[i, j].Green = bits[bitIndex + 1];
#endif
                    mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;

                }
                content.Append("\n");
            }
            //File.WriteAllText("out.txt", content.ToString());
            dataChanged = true;
        }

        bool isDrag = false;
        Point itemPosition = new Point(0, 0);
        Bitmap currentBitmap;
        public void initWithBitmap(Bitmap bitmap)
        {
            bitmap.Save("out.bmp");
            // reject bitmap larger than led panel size
            if (mXPixels < bitmap.Width) return;
            if (mYPixels < bitmap.Height) return;
            currentBitmap = bitmap;
            // copy pixel from bitmap to led bit matrix
            for (int j = 0; j < mYPixels; j++)
            {
                for (int i = 0; i < mXPixels; i++)
                {
                    if (i < bitmap.Width && j < bitmap.Height)
                    {
                        Color col = bitmap.GetPixel(i, j);
                        if (col.ToArgb() == Color.Red.ToArgb())
                        {
                            mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_RED;
                        }
                        //else if (col.ToArgb() == Color.FromArgb(255,0,255,0).ToArgb())
                        else if (col.ToArgb() == Color.Lime.ToArgb())
                        {
                            mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_GREEN;
                        }
                        else if (col.ToArgb() == Color.Yellow.ToArgb())
                        {
                            mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_YELLOW;
                        }
                        else
                        {
                            mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;
                        }
                    }
                    else
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;
                    }
                }
            }
            dataChanged = true;
            this.Invalidate();

        }

        public static byte[] getByteFromBitmap(Bitmap bitmap)
        {
            // alloc memory array
            //LED_PIXEL_3COLOR[,] pixels = new LED_PIXEL_3COLOR[bitmap.Width, bitmap.Height];
            BitArray bArray = new BitArray(bitmap.Width * bitmap.Height * BPP);
            LED_PIXEL_3COLOR pixel = LED_PIXEL_3COLORTABLE_NONE;
            // copy pixel from bitmap to led bit matrix
            for (int j = 0; j < bitmap.Width; j++)
            {
                for (int i = 0; i < bitmap.Height; i++)
                {
                    Color col = bitmap.GetPixel( j,i);
                    if (col.ToArgb() == Color.Red.ToArgb())
                    {
                        pixel = LED_PIXEL_3COLORTABLE_RED;
                    }
                    //else if (col.ToArgb() == Color.FromArgb(255, 0, 255, 0).ToArgb())
                    else if (col.ToArgb() == Color.Lime.ToArgb())
                    {
                        pixel = LED_PIXEL_3COLORTABLE_GREEN;
                    }
                    else if (col.ToArgb() == Color.Yellow.ToArgb())
                    {
                        pixel = LED_PIXEL_3COLORTABLE_YELLOW;
                    }
                    else
                    {
                        pixel = LED_PIXEL_3COLORTABLE_NONE;
                    }
                    try
                    {
                        // copy data from led matrix array to buffer.
                        int j1 = j / 2;
                        bArray[i * bitmap.Width * BPP + j] = pixel.Red;
                        bArray[i * bitmap.Width * BPP + j + 1] = pixel.Green;
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine(e.Message);
                    }
                }
            }
            /*

            // create bit array to hold bit matrix
            // Data format:
            //   <pixel>   <pixel>......<pixel>
            // bool,bool,bool,bool,...bool,bool.
            // each pixel has bool pair values: <bool,bool>
            BitArray bArray = new BitArray(bitmap.Width * bitmap.Height * BPP);
            for (int i = 0; i < bitmap.Height; i++)
            {
                for (int j = 0; j < bitmap.Width * BPP; j += BPP)
                {
                    // copy data from led matrix array to buffer.
                    int j1 = j / 2;
                    bArray[i * bitmap.Width * BPP + j] = pixels[j1, i].Red;
                    bArray[i * bitmap.Width * BPP + j + 1] = pixels[j1, i].Green;
                }
            }
             * */
            byte[] ret = new byte[(bArray.Length+7) / 8 + 5];

            // Data format:
            // <width><height><BPP><data>

            // <width> 2 bytes short
            byte[] temp = System.BitConverter.GetBytes((ushort)bitmap.Width);
            ret[0] = temp[0];
            ret[1] = temp[1];
            // <height> 2 bytes short
            temp = System.BitConverter.GetBytes((ushort)bitmap.Height);
            ret[2] = temp[0];
            ret[3] = temp[1];
            // <BPP> 1 bytes short 
            // bpp can set to 1,2 or 24 bits per pixel
            ret[4] = 2;
            // <data>  bArray.Length/8 bytes
            bArray.CopyTo(ret, 5);
            //string hexString = ByteArrayToString(ret, 16);
            //hexString.Replace("", ", 0x");
            //File.WriteAllText("led.hex", hexString);
            //return ret;

            try
            {
                // Open file for reading
                System.IO.FileStream _FileStream = new System.IO.FileStream("led.bin", System.IO.FileMode.Create, System.IO.FileAccess.Write);

                // Writes a block of bytes to this stream using data from a byte array.
                _FileStream.Write(ret, 0, ret.Length);

                // close file stream
                _FileStream.Close();

            }
            catch (Exception _Exception)
            {
                // Error
                Debug.WriteLine("Exception caught in process: {0}", _Exception.ToString());
            }

            // error occured, return false
            return ret;
        }

        // background color change event, update led color
        private void LETILed_BackColorChanged(object sender, EventArgs e)
        {
            return;
            Color color = this.BackColor;
            for (int i = 0; i < mXPixels; i++)
            {
                for (int j = 0; j < mYPixels; j++)
                {
                    if (color == Color.Red)
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_RED;
                    }
                    else if (color == Color.Lime)
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_GREEN;
                    }
                    else if (color == Color.Yellow)
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_YELLOW;
                    }
                    else
                    {
                        mLedMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;
                    }
                }
            }
        }

        /// <summary>
        /// Event to blink bad led
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        void tmBlink_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (blinking == false)
            {
                tmBlink.Stop();
                tmBlink.AutoReset = false;
            }
            else
            {
                blinkingFlag = !blinkingFlag;
                Invalidate();
            }
        }

        Stopwatch _stopwatch = new Stopwatch();
        double delayTime = 0;

        [Browsable(true)]
        [Category("Led Config")]
        public int FrameRate { get; set; }
        int _frameCounter = 0;
        bool isFirst = true;
        Brush getBrushByColor(LED_PIXEL_3COLOR color)
        {            
            if(color ==  LED_PIXEL_3COLORTABLE_RED)
                return Brushes.Red;
            else if(color ==  LED_PIXEL_3COLORTABLE_GREEN)
                return Brushes.Green;
            else if(color ==  LED_PIXEL_3COLORTABLE_YELLOW)
                return Brushes.Yellow;
            else 
                return Brushes.Gray;
            
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
#if RENDER_TEST
            return;
#else
            if (_frameCounter++ == 0)
            {
                // Starting timing.
                _stopwatch.Start();
            }

            // Determine frame rate in fps (frames per second).
            long frameRate = (long)(_frameCounter / this._stopwatch.Elapsed.TotalSeconds);
            //System.Debug.WriteLine(frameRate);
            //if (frameRate > 0 && frameRate < FrameRate || isFirst)
            {
                isFirst = false;
                Rectangle cRect = ClientRectangle;
                // calculate scale factor
                float fScaleRatioX = (Width - Margin.Horizontal) / (mXPixels + 2 * mPixelGapX);
                float fScaleRatioY = (Height - Margin.Vertical) / (mYPixels + 2 * mPixelGapY);
                // position in pixel
                float posX = 0;
                float posY = 0;

                int itemWidth = mLedItemMatrix.GetLength(0);
                int itemHeight = mLedItemMatrix.GetLength(1);

                // Graphic object
                Graphics graphic = pe.Graphics;

                // Iterate for each pixel
                for (int i = 0; i < mXPixels; i++)
                {
                    // X position 
                    posX = (float)(fScaleRatioX * i + Margin.Left);
                    for (int j = 0; j < mYPixels; j++)
                    {
                        // Y position
                        posY = (float)(fScaleRatioY * j + Margin.Top);

                        // Draw led status first
#if !TOANNV
                        if (mLedStatusMatrix[i, j] == true)
                        {
                            // check for blinking flag
                            if (!blinkingFlag)
                                graphic.FillEllipse(Brushes.Violet, posX, posY, fScaleRatioX, fScaleRatioY);
                            else
                                graphic.FillEllipse(Brushes.Gray, posX, posY, fScaleRatioX, fScaleRatioY);
                        }
#else
                        if (mLedStatusMatrix[i, j] == LED_PIXEL_3COLORTABLE_RED)
                        {
                            // check for blinking flag
                            if (!blinkingFlag)
                                graphic.FillEllipse(Brushes.Violet, posX, posY, fScaleRatioX, fScaleRatioY);
                            else
                                graphic.FillEllipse(Brushes.Red, posX, posY, fScaleRatioX, fScaleRatioY);
                        }
                        else if (mLedStatusMatrix[i, j] == LED_PIXEL_3COLORTABLE_GREEN)
                        {
                            // check for blinking flag
                            if (!blinkingFlag)
                                graphic.FillEllipse(Brushes.Violet, posX, posY, fScaleRatioX, fScaleRatioY);
                            else
                                graphic.FillEllipse(Brushes.Lime, posX, posY, fScaleRatioX, fScaleRatioY);
                        }
                        else if (mLedStatusMatrix[i, j] == LED_PIXEL_3COLORTABLE_YELLOW)
                        {
                            // check for blinking flag
                            if (!blinkingFlag)
                                graphic.FillEllipse(Brushes.Violet, posX, posY, fScaleRatioX, fScaleRatioY);
                            else
                                graphic.FillEllipse(Brushes.Yellow, posX, posY, fScaleRatioX, fScaleRatioY);
                        }
#endif
                        else
                        {
                            // draw led data
                            if (dataChanged && mLedMatrix[i, j] == LED_PIXEL_3COLORTABLE_RED)
                            {
                                // check for dragging operation
                                if (isDrag)
                                {
                                    // draw dragged item's pixel color
                                    if (i >= itemPosition.X && i < itemPosition.X + itemWidth &&
                                        j >= itemPosition.Y && j < itemPosition.Y + itemHeight)
                                    {
                                        graphic.FillEllipse(getBrushByColor(mLedItemMatrix[i-itemPosition.X,j-itemPosition.Y]), 
                                            posX, posY, 
                                            fScaleRatioX, fScaleRatioY);
                                    }
                                    else
                                        graphic.FillEllipse(Brushes.Red, posX, posY, fScaleRatioX, fScaleRatioY);
                                }
                                else // draw red pixel
                                    graphic.FillEllipse(Brushes.Red, posX, posY, fScaleRatioX, fScaleRatioY);
                                
                            }
                            else if (dataChanged && mLedMatrix[i, j] == LED_PIXEL_3COLORTABLE_GREEN)
                            {
                                // check for dragging operation
                                if (isDrag)
                                {
                                    if (i >= itemPosition.X && i < itemPosition.X + itemWidth &&
                                        j >= itemPosition.Y && j < itemPosition.Y + itemHeight)
                                    {
                                        graphic.FillEllipse(getBrushByColor(mLedItemMatrix[i - itemPosition.X, j - itemPosition.Y]),
                                            posX, posY,
                                            fScaleRatioX, fScaleRatioY);
                                    }
                                    else // draw dragged item's pixel color
                                        graphic.FillEllipse(Brushes.Lime, posX, posY, fScaleRatioX, fScaleRatioY);
                                }
                                else // draw green pixel
                                    graphic.FillEllipse(Brushes.Lime, posX, posY, fScaleRatioX, fScaleRatioY);
                            }
                            else if (dataChanged && mLedMatrix[i, j] == LED_PIXEL_3COLORTABLE_YELLOW)
                            {
                                // check for dragging operation
                                if (isDrag)
                                {
                                    if (i >= itemPosition.X && i < itemPosition.X + itemWidth &&
                                        j >= itemPosition.Y && j < itemPosition.Y + itemHeight)
                                    {
                                        graphic.FillEllipse(getBrushByColor(mLedItemMatrix[i - itemPosition.X, j - itemPosition.Y]),
                                            posX, posY,
                                            fScaleRatioX, fScaleRatioY);
                                    }
                                    else // draw dragged item's pixel color
                                        graphic.FillEllipse(Brushes.Yellow, posX, posY, fScaleRatioX, fScaleRatioY);
                                }
                                else // draw yellow pixel
                                    graphic.FillEllipse(Brushes.Yellow, posX, posY, fScaleRatioX, fScaleRatioY);
                            }
                            else if (dataChanged && mLedMatrix[i, j] == LED_PIXEL_3COLORTABLE_NONE)
                            {
                                // check for dragging operation
                                if (isDrag)
                                {
                                    if (i >= itemPosition.X && i < itemPosition.X + itemWidth &&
                                        j >= itemPosition.Y && j < itemPosition.Y + itemHeight)
                                    {
                                        graphic.FillEllipse(getBrushByColor(mLedItemMatrix[i - itemPosition.X, j - itemPosition.Y]),
                                            posX, posY,
                                            fScaleRatioX, fScaleRatioY);
                                    }
                                    else // draw dragged item's pixel color
                                        graphic.FillEllipse(Brushes.Gray, posX, posY, fScaleRatioX, fScaleRatioY);
                                }
                                else // draw background pixel (gray color)
                                    graphic.FillEllipse(Brushes.Gray, posX, posY, fScaleRatioX, fScaleRatioY);
                            }
                        }
                    }
                }
                dataChanged = true;
            }
#endif
        }

        private void LETILed_Resize(object sender, EventArgs e)
        {
            if (Width % mXPixels != 0 && Height % mYPixels != 0)
            {
                Padding pad = new Padding();
                pad.Left = (int)(Width - (int)(Width / mXPixels) * mXPixels) / 2;
                pad.Right = (int)(Width - (int)(Width / mXPixels) * mXPixels) / 2;
                pad.Top = (int)(Height - (int)(Height / mYPixels) * mYPixels) / 2;
                pad.Bottom = (int)(Height - (int)(Height / mYPixels) * mYPixels) / 2;
                Margin = pad;
                this.Invalidate();
            }
        }

        #region | get temp data |
        public byte[] getBytes()
        {
            // get size of led color pixel
            byte size;
            unsafe
            {
                size = (byte)sizeof(LED_PIXEL_3COLOR);
            }

            // create bit array to hold bit matrix
            // Data format:
            //   <pixel>   <pixel>......<pixel>
            // bool,bool,bool,bool,...bool,bool.
            // each pixel has bool pair values: <bool,bool>
            BitArray bArray = new BitArray(mXPixels * mYPixels * size);
            for (int i = 0; i < mYPixels; i++)
            {
                for (int j = 0; j < mXPixels * size; j += size)
                {
                    // copy data from led matrix array to buffer.
                    int j1 = j / 2;
                    bArray[i * mXPixels * size + j] = mLedMatrix[j1, i].Red;
                    bArray[i * mXPixels * size + j + 1] = mLedMatrix[j1, i].Green;
                }
            }
            byte[] hexBuffer = new byte[bArray.Length / 8];
            // save as hex string
            bArray.CopyTo(hexBuffer, 0);

            // Convert the binary input into Base64 UUEncoded output.
            string base64String;
            try
            {
                base64String =
                   System.Convert.ToBase64String(hexBuffer,
                                                 0,
                                                 hexBuffer.Length);
                File.WriteAllText("LedData.txt", base64String);
            }
            catch (System.ArgumentNullException)
            {
                System.Console.WriteLine("Binary data array is null.");
                return hexBuffer;
            }

            return hexBuffer;
        }

        public byte[] getBankArray()
        {
            // create bit array to hold bit matrix
            // Data format:
            //   <pixel>   <pixel>......<pixel>
            // bool,bool,bool,bool,...bool,bool.
            // each pixel has bool pair values: <bool,bool>
            BitArray bArray = new BitArray(mXPixels * mYPixels * BPP);
            for (int i = 0; i < mYPixels; i++)
            {
                for (int j = 0; j < mXPixels * BPP; j += BPP)
                {
                    // copy data from led matrix array to buffer.
                    int j1 = j / 2;
                    bArray[i * mXPixels * BPP + j] = false;
                    bArray[i * mXPixels * BPP + j + 1] = false;
                }
            }
            byte[] hexBuffer = new byte[bArray.Length / 8];
            // save as hex string
            bArray.CopyTo(hexBuffer, 0);
            return hexBuffer;
        }

        public byte[] getRedArray()
        {
            // create bit array to hold bit matrix
            // Data format:
            //   <pixel>   <pixel>......<pixel>
            // bool,bool,bool,bool,...bool,bool.
            // each pixel has bool pair values: <bool,bool>
            BitArray bArray = new BitArray(mXPixels * mYPixels * BPP);
            for (int i = 0; i < mYPixels; i++)
            {
                for (int j = 0; j < mXPixels * BPP; j += BPP)
                {
                    // copy data from led matrix array to buffer.
                    int j1 = j / 2;
                    bArray[i * mXPixels * BPP + j] = true;
                    bArray[i * mXPixels * BPP + j + 1] = false;
                }
            }
            byte[] hexBuffer = new byte[bArray.Length / 8];
            // save as hex string
            bArray.CopyTo(hexBuffer, 0);
            return hexBuffer;
        }

        public byte[] getGreenArray()
        {
            // create bit array to hold bit matrix
            // Data format:
            //   <pixel>   <pixel>......<pixel>
            // bool,bool,bool,bool,...bool,bool.
            // each pixel has bool pair values: <bool,bool>
            BitArray bArray = new BitArray(mXPixels * mYPixels * BPP);
            for (int i = 0; i < mYPixels; i++)
            {
                for (int j = 0; j < mXPixels * BPP; j += BPP)
                {
                    // copy data from led matrix array to buffer.
                    int j1 = j / 2;
                    bArray[i * mXPixels * BPP + j] = false;
                    bArray[i * mXPixels * BPP + j + 1] = true;
                }
            }
            byte[] hexBuffer = new byte[bArray.Length / 8];
            // save as hex string
            bArray.CopyTo(hexBuffer, 0);
            return hexBuffer;
        }

        public byte[] getYellowArray()
        {
            // create bit array to hold bit matrix
            // Data format:
            //   <pixel>   <pixel>......<pixel>
            // bool,bool,bool,bool,...bool,bool.
            // each pixel has bool pair values: <bool,bool>
            BitArray bArray = new BitArray(mXPixels * mYPixels * BPP);
            for (int i = 0; i < mYPixels; i++)
            {
                for (int j = 0; j < mXPixels * BPP; j += BPP)
                {
                    // copy data from led matrix array to buffer.
                    int j1 = j / 2;
                    bArray[i * mXPixels * BPP + j] = true;
                    bArray[i * mXPixels * BPP + j + 1] = true;
                }
            }
            byte[] hexBuffer = new byte[bArray.Length / 8];
            // save as hex string
            bArray.CopyTo(hexBuffer, 0);
            return hexBuffer;
        }
        #endregion
        public void saveToFile()
        {
            // get size of led color pixel
            byte size;
            unsafe
            {
                size = (byte)sizeof(LED_PIXEL_3COLOR);
            }

            // create bit array to hold bit matrix
            // Data format:
            //   <pixel>   <pixel>......<pixel>
            // bool,bool,bool,bool,...bool,bool.
            // each pixel has bool pair values: <bool,bool>
            BitArray bArray = new BitArray(mXPixels * mYPixels * size);
            for (int i = 0; i < mYPixels; i++)
            {
                for (int j = 0; j < mXPixels * size; j += size)
                {
                    // copy data from led matrix array to buffer.
                    int j1 = j / 2;
                    bArray[i * mXPixels*size + j] = mLedMatrix[j1, i].Red;
                    bArray[i * mXPixels*size + j + 1] = mLedMatrix[j1, i].Green;
                }
            }
            //PrintLED(mLedMatrix, mXPixels, mYPixels);
            //PrintValues(bArray, mXPixels*size);
            // alloc byte buffer
#if !TEST
#else
            byte[] hexBuffer = new byte[bArray.Length / 8];
             // save as hex string
             bArray.CopyTo(hexBuffer, 0);
             //PrintValues(bArray, 128);
             //string hexString = ByteArrayToString(hexBuffer, 16);
             //File.WriteAllText("led.hex", hexString);
//#else
            byte[] ret = new byte[bArray.Length / 8 + 5];
           
            // Data format:
            // <width><height><BPP><data>

            // <width> 2 bytes short
            byte[] temp = System.BitConverter.GetBytes(mXPixels);
            ret[0] = temp[0];
            ret[1] = temp[1];
            // <height> 2 bytes short
            temp = System.BitConverter.GetBytes(mYPixels);
            ret[2] = temp[0];
            ret[3] = temp[1];
            // <BPP> 1 bytes short 
            // bpp can set to 1,2 or 24 bits per pixel
            ret[4] = size;
            // <data>  bArray.Length/8 bytes
            bArray.CopyTo(ret, 5);
            ByteArrayToFile("led.bin", ret);
#endif
        }

        public static void PrintLED(LED_PIXEL_3COLOR[,] myList, int myWidth,int myHeight)
        {
            for (int j = 0; j < myHeight; j++)
            {
                for (int i = 0; i < myWidth; i++)
                {
                    Console.Write("{0,6}{0,6}", myList[i, j].Red, myList[i, j].Green);
                }
                Debug.WriteLine("");
            }
        }

        public static void PrintValues(IEnumerable myList, int myWidth)
        {
            int i = myWidth;
            foreach (Object obj in myList)
            {
                if (i <= 0)
                {
                    i = myWidth;
                    Debug.WriteLine("");
                }
                i--;
                Console.Write("{0,6}", obj);
            }
            Debug.WriteLine("");
        }

        public static string ByteArrayToString(byte[] ba, int wordCount)
        {
            StringBuilder hex = new StringBuilder(ba.Length * 2);
            int i = 0;
            foreach (byte b in ba)
            {
                i++;
                if (i % 16 == 0)
                    hex.AppendFormat("0x{0:x2}, \n ", b);
                else
                    hex.AppendFormat("0x{0:x2}, ", b);

            }
            return hex.ToString();
        }

        public bool ByteArrayToFile(string _FileName, byte[] _ByteArray)
        {
            try
            {
                // Open file for reading
                System.IO.FileStream _FileStream = new System.IO.FileStream(_FileName, System.IO.FileMode.Create, System.IO.FileAccess.Write);

                // Writes a block of bytes to this stream using data from a byte array.
                _FileStream.Write(_ByteArray, 0, _ByteArray.Length);

                // close file stream
                _FileStream.Close();

                return true;
            }
            catch (Exception _Exception)
            {
                // Error
                Debug.WriteLine("Exception caught in process: {0}", _Exception.ToString());
            }

            // error occured, return false
            return false;
        }
        
        int currentColorIntdex = 1;
        public enum TOOL{
            NONE,
            ERASE,
            TEXT,
            PENCIL,
            MAGIC
        }
        public TOOL CurrentTool { get; set; }

        private void LETILed_MouseDown(object sender, MouseEventArgs e)
        {
            isDrag = true;
            switch (CurrentTool)
            {
                case TOOL.ERASE:
                    // TODO: process image here
                    doErase(e.Location);
                    break;
                case TOOL.TEXT:
                    // TODO: perform text operation
                    doAddText(e.Location);
                    break;
                case TOOL.PENCIL:
                    // TODO: perform stoke operation
                    break;
                case TOOL.MAGIC:
                    // TODO: perform magic fill
                    doMagicTool(e.Location);
                    break;
                case TOOL.NONE:
                    //OnObjectSelected(null);
                    break;
            }
        }

        private void LETILed_MouseMove(object sender, MouseEventArgs e)
        {
            if (!isDrag) return;
            switch (CurrentTool)
            {
                case TOOL.ERASE:
                    // TODO: process image here
                    doErase(e.Location);
                    break;
                case TOOL.TEXT:
                    // TODO: perform text operation
                    break;
                case TOOL.PENCIL:
                    // TODO: perform stoke operation
                    break;
                case TOOL.MAGIC:
                    // TODO: perform magic fill
                    // do nothing
                    break;
            }
            /*
            dataChanged = true;
            isDrag = true;
            itemPosition = this.PointToClient(e.Location);
            Invalidate();
             * */
        }

        private void LETILed_MouseUp(object sender, MouseEventArgs e)
        {
            //return;
            dataChanged = true;
            isDrag = false;
            itemPosition = this.PointToClient(e.Location);
            Invalidate();
        }

        #region | TOOL functions |
        private void doMagicTool(Point location)
        {
            if (currentColorIntdex > 3)
                currentColorIntdex = 1;

            switch (currentColorIntdex)
            {
                case 1:
                    floodFilter.FillColor = Color.Red;
                    break;
                case 2:
                    floodFilter.FillColor = Color.Lime;
                    break;
                case 3:
                    floodFilter.FillColor = Color.Yellow;
                    break;
                default:
                    floodFilter.FillColor = Color.Gray;
                    break;
            }
            floodFilter.FillDiagonal = true;
            floodFilter.FillStyle = FloodFillStyle.Linear;
            floodFilter.Bmp = (Bitmap)currentBitmap;

            floodFilter.Pt = PointToLed(location);

            floodFilter.FloodFill();

            initWithBitmap(floodFilter.Bmp);
            currentColorIntdex++;

        }

        private void doErase(Point location)
        {
            Point ledPos = PointToLed(location);

            mLedMatrix[ledPos.X, ledPos.Y] = LED_PIXEL_3COLORTABLE_NONE;
            dataChanged = true;
            Invalidate();
        }
        //List<ObjectItem> objlst = new List<ObjectItem>();

        private void doAddText(Point location)
        {
            /*
            if (objlst.Count >= 5)
            {
                MessageBox.Show("Warning", "Hiện tại chỉ hỗ trợ tối đa 5 đối tượng!\n Hãy xoá bớt đối tượng đi!");
                return;
            }
             * */
            // reset current tool

            CurrentTool = TOOL.NONE;
            Point ledPos = PointToLed(location);
            //this.cMenuObject.Show(location);
            float fScaleRatioX = (Width - Margin.Horizontal) / (mXPixels + 2 * mPixelGapX);
            float fScaleRatioY = (Height - Margin.Vertical) / (mYPixels + 2 * mPixelGapY);
            //ObjectItem obj = new ObjectItem(new Size((int)fScaleRatioX, (int)fScaleRatioY));
            //obj.Click += new EventHandler(obj_Click);
            //obj.Location = location;
            /*
            // reset object state
            foreach (ObjectItem o in objlst)
            {
                o.Selected = false;
                o.Invalidate();
            }

            objlst.Add(obj);
            
            this.Controls.Add(obj);
            // select object
            OnObjectSelected(obj.Model);
            */
            //propertyGrid.Location = location;
            //propertyGrid.SelectedObject = obj;
            //propertyGrid.Show();
            Invalidate();
        }

        public Point PointToLed(Point position)
        {
            float fScaleRatioX = (Width - Margin.Horizontal) / (mXPixels + 2 * mPixelGapX);
            float fScaleRatioY = (Height - Margin.Vertical) / (mYPixels + 2 * mPixelGapY);
            int posX = 0;
            int posY = 0;
            posX = (int)((position.X - Margin.Left) / fScaleRatioX);
            posY = (int)((position.Y - Margin.Top) / fScaleRatioY);
            return new Point(posX, posY);
        }
        #endregion
        private void LETILed_DragEnter(object sender, DragEventArgs e)
        {
            return; // temporally disable
            // As we are interested in Image data only
            // we will check this as follows
            if (e.Data.GetDataPresent(typeof(Bitmap)))
            {
                e.Effect = DragDropEffects.Copy;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
            isDrag = true;
            updateLedItem((Bitmap)e.Data.GetData(typeof(Bitmap)));
            Invalidate();
        }

        private void LETILed_DragDrop(object sender, DragEventArgs e)
        {
            return; // temporally disable
            //target control will accept data here 
            //Panel destination = (Panel)sender;
            //destination.BackgroundImage = (Bitmap)e.Data.GetData(typeof(Bitmap));
            itemPosition = this.PointToClient(new Point(e.X, e.Y));
            //itemPosition = (e.Location);

            float fScaleRatioX = (Width - Margin.Horizontal) / (mXPixels + 2 * mPixelGapX);
            float fScaleRatioY = (Height - Margin.Vertical) / (mYPixels + 2 * mPixelGapY);
            int posX = 0;
            int posY = 0;
            posX = (int)((itemPosition.X - Margin.Left) / fScaleRatioX);
            posY = (int)((itemPosition.Y - Margin.Top) / fScaleRatioY);

            itemPosition.X = posX;
            itemPosition.Y = posY;
            isDrag = false;
            finishDrop();
            Invalidate();
        }

        private void finishDrop()
        {
            return; // temporally disable
            int itemWidth = mLedItemMatrix.GetLength(0);
            int itemHeight = mLedItemMatrix.GetLength(1);
            for (int j = 0; j < mYPixels; j++)
                for (int i = 0; i < mXPixels; i++)
                {
                    if (i >= itemPosition.X && i < itemPosition.X + itemWidth &&
                        j >= itemPosition.Y && j < itemPosition.Y + itemHeight)
                    {
                        mLedMatrix[i, j] = mLedItemMatrix[i - itemPosition.X, j - itemPosition.Y];
                    }
                }
            dataChanged = true;
            Invalidate();
        }

        private void LETILed_DragOver(object sender, DragEventArgs e)
        {
            return; // temporaly disable
            dataChanged = true;
            
            itemPosition = this.PointToClient(new Point(e.X,e.Y));
            //itemPosition = (e.Location);

            float fScaleRatioX = (Width - Margin.Horizontal) / (mXPixels + 2 * mPixelGapX);
            float fScaleRatioY = (Height - Margin.Vertical) / (mYPixels + 2 * mPixelGapY);
            int posX = 0;
            int posY = 0;
            posX = (int)((itemPosition.X - Margin.Left) / fScaleRatioX);
            posY = (int)((itemPosition.Y - Margin.Top) / fScaleRatioY);

            itemPosition.X = posX;
            itemPosition.Y = posY;
            Debug.WriteLine(itemPosition);
            Invalidate();
        }

        public void updateLedItem(Bitmap bitmap)
        {
            mLedItemMatrix = new LED_PIXEL_3COLOR[bitmap.Width,bitmap.Height];

            // copy pixel from bitmap to led bit matrix
            for (int j = 0; j < bitmap.Height; j++)
            {
                for (int i = 0; i < bitmap.Width; i++)
                {
                    if (i < bitmap.Width && j < bitmap.Height)
                    {
                        Color col = bitmap.GetPixel(i, j);
                        if (col.ToArgb() == Color.Red.ToArgb())
                        {
                            mLedItemMatrix[i, j] = LED_PIXEL_3COLORTABLE_RED;
                        }
                        //else if (col.ToArgb() == Color.Green.ToArgb())
                        else if (col.ToArgb() == Color.Green.ToArgb())
                        {
                            mLedItemMatrix[i, j] = LED_PIXEL_3COLORTABLE_GREEN;
                        }
                        else if (col.ToArgb() == Color.Yellow.ToArgb())
                        {
                            mLedItemMatrix[i, j] = LED_PIXEL_3COLORTABLE_YELLOW;
                        }
                        else
                        {
                            mLedItemMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;
                        }
                    }
                    else
                    {
                        mLedItemMatrix[i, j] = LED_PIXEL_3COLORTABLE_NONE;
                    }
                }
            }
        }
        /*
        void obj_Click(object sender, EventArgs e)
        {
            // reset selected state
            foreach (ObjectItem obj in objlst)
            {
                obj.Selected = false;
                obj.Invalidate();
            }
            SelectedItem = (ObjectItem)sender;
            SelectedItem.Selected = true;
            
            SelectedItem.Invalidate();
            OnObjectSelected(SelectedItem.Model);
        }
         * */
        //ObjectItem SelectedItem;
        //public delegate void ObjectSelectedHandler(ObjectItemModel model);
        //public event ObjectSelectedHandler ObjectSelected;
        /*
        protected void OnObjectSelected(ObjectItemModel model)
        {
            if (model == null)
            {
                SelectedItem = null;
                foreach(ObjectItem obj in objlst)
                {
                    obj.Selected = false;
                    obj.Invalidate();
                }
            }
            ObjectSelected(model);
        }
        */
        public int Index { get; set; }
    }
}
